#006 Asset Allocation with Random Portfolio Weights¶

In this code, I'll define a function to generate a random portfolio weights, perform an asset allocation, and then and analyze their returns.

First for a equal weighted portfolio and then for random weights, define a function to concatenate portfolios into a DataFrame and finaly plotly data

libs¶

Install Libs. (remove comments '#' if need to install the libraries)

In [1]:
#    !pip install pandas
#    !pip install pandas-datareader
#    !pip install numpy
#    !pip install plotly_express
#    !pip install random
In [2]:
#import Libraries
import pandas as pd
from pandas_datareader import data as pdr
import numpy as np
import random
import plotly.graph_objects as go

Functions¶

functions already defined that we use on this code

In [3]:
# Define a function using Plotly Express
def plotly_data(df, title):  
    
    # Create figure
    fig = go.Figure()
   
    # Set title
    fig.update_layout(title_text = title) 
    
    # For loop that plots all stock prices in the pandas dataframe df
    
    for i in df.columns[0:]:
        # Add range slider
        #fig.update_layout(xaxis=dict(rangeselector = dict(buttons=list([dict(count=1, label="1m", step="month", stepmode="backward"), dict(count=6, label="6m", step="month", stepmode="backward"), dict(count=1, label="YTD", step="year", stepmode="todate"), dict(count=1, label="1y", step="year", stepmode="backward"), dict(step="all")])), rangeslider=dict( visible=True), type="date"))
        # Add line graph
        fig.add_scatter(x = df.index, y = df[i], name = i)
        # Update Layout
        fig.update_layout({'plot_bgcolor': "white"})
        #fig.update_traces(line_width = 3)
        fig.update_layout(legend=dict(orientation="h",)) 
        
    fig.show()
    
# Define a function using Plotly Express, changes axis y to logarithm scale
def log_plotly_data(df, title):  
    
    # Create figure
    fig = go.Figure()
   
    # Set title
    fig.update_layout(title_text = title) 
    
    # For loop that plots all stock prices in the pandas dataframe df
    
    for i in df.columns[0:]:
        # Add range slider
        #fig.update_layout(xaxis=dict(rangeselector = dict(buttons=list([dict(count=1, label="1m", step="month", stepmode="backward"), dict(count=6, label="6m", step="month", stepmode="backward"), dict(count=1, label="YTD", step="year", stepmode="todate"), dict(count=1, label="1y", step="year", stepmode="backward"), dict(step="all")])), rangeslider=dict( visible=True), type="date"))
        # Add line graph
        fig.add_scatter(x = df.index, y = df[i], name = i)
        # Update Layout
        fig.update_layout({'plot_bgcolor': "white"})
        #fig.update_traces(line_width = 3)
        fig.update_layout(legend=dict(orientation="h",)) 
    
    #changes y to logarithm scale
    fig.update_yaxes(type="log")
    fig.show()
    
# Define a function using Plotly Express, changes axis y to logarithm scale
def plotly_line(df, y, title):  
    
    # Create figure
    fig = go.Figure()
    fig.update_layout(title_text = title) 
    fig.add_scatter(x = df.index, y = y)
    # Update Layout
    fig.update_layout({'plot_bgcolor': "white"})
    #fig.update_traces(line_width = 3)
    fig.update_layout(legend=dict(orientation="h",)) 
    
    #changes y to logarithm scale
    fig.show()    
        
# Define a function using Plotly Express, changes axis y to logarithm scale
def log_plotly_line(df, y, title):  
    
    # Create figure
    fig = go.Figure()
    fig.update_layout(title_text = title) 
    fig.add_scatter(x = df.index, y = y)
    # Update Layout
    fig.update_layout({'plot_bgcolor': "white"})
    #fig.update_traces(line_width = 3)
    fig.update_layout(legend=dict(orientation="h",)) 
    
    #changes y to logarithm scale
    fig.update_yaxes(type="log")
    fig.show()    
    
In [4]:
# Function to scale stock prices based on their initial starting price
# The objective of this function is to set all prices to start at a value of 1 
def price_scaling(raw_prices_df):
    scaled_prices_df = raw_prices_df.copy()
    for i in raw_prices_df.columns[0:]:
          scaled_prices_df[i] = raw_prices_df[i]/raw_prices_df[i][0]
    return scaled_prices_df

6.1 Equal Weighted Portfolio¶

In [5]:
file_name = input('Input the CSV file name: ')
initial_investment = int(input('Input the initial investment: '))
n_runs = int(input('Input the number of simulations: '))
Input the CSV file name: BRL10
Input the initial investment: 1000000
Input the number of simulations: 20
In [6]:
#read CSV file
Stock_Prices_df = pd.read_csv(file_name)
#The code imports a DataFrame with num index [1,2,3...], this line replace the colum Date to Index
Stock_Prices_df.set_index(['Date'], inplace = True)
In [7]:
#obtain weights vector
n_assets = len(Stock_Prices_df.columns)
#lock vector to test function
weights = np.ones(n_assets) * 1/n_assets
weights
Out[7]:
array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])
In [8]:
#Define a function that performs an Asset Allocation
def asset_allocation(df, initial_investment, weights):
    ''' Performs an asset Allocation for a given DF, initial investment value and weights'''
    
    portfolio_df = df.copy()
    
    # Scale stock prices using the "price_scaling"
    scaled_df = price_scaling(df)
    
    #enumerate method links Stocks tickers in columns along with a counter position weight (i), like an index
    for i, stock in enumerate(scaled_df):
        portfolio_df[stock] = weights[i] * scaled_df[stock]  * initial_investment
    
    # Sum up all values and place the result in a new column titled "portfolio value [$]" 
    portfolio_df['Total Value [$]'] = portfolio_df.sum(axis = 1, numeric_only = True)
    
    # Calculate the portfolio percentage daily return and replace NaNs with zeros
    portfolio_df['Daily Return [%]'] = portfolio_df['Total Value [$]'].pct_change(1) * 100 
    portfolio_df.replace(np.nan, 0, inplace = True)
    
    return portfolio_df
In [9]:
#Asset Allocation with parameters defined
   
portfolio_df = asset_allocation(Stock_Prices_df, initial_investment, weights)
Eqw = portfolio_df
portfolio_df.round(2)
Out[9]:
CPLE6.SA CRFB3.SA ECOR3.SA ITUB4.SA JBSS3.SA PRIO3.SA RENT3.SA SBSP3.SA VALE3.SA VIVT3.SA Total Value [$] Daily Return [%]
Date
2018-12-14 100000.00 100000.00 100000.00 100000.00 100000.00 100000.00 100000.00 100000.00 100000.00 100000.00 1000000.00 0.00
2018-12-17 99692.31 99555.55 97731.96 97325.43 98509.95 104978.13 97950.53 98694.52 100727.19 99056.60 994222.16 -0.58
2018-12-18 99384.62 100833.31 97422.68 98904.25 98675.50 98269.33 97597.19 98074.41 101100.60 100856.98 991118.87 -0.31
2018-12-19 96553.83 97333.32 96907.21 97580.61 95529.80 99183.28 98339.21 97225.86 98290.08 98575.67 975518.87 -1.57
2018-12-20 96461.52 100333.34 97113.40 97552.45 94370.86 96246.96 99346.72 99869.46 97897.01 97278.95 976470.66 0.10
... ... ... ... ... ... ... ... ... ... ... ... ...
2023-12-07 469772.65 69389.34 95695.89 108459.32 247430.61 2062226.53 244892.92 248979.82 209180.82 181340.74 3937368.64 1.12
2023-12-08 470264.01 67795.60 95904.60 109831.35 250366.22 2165775.45 243260.03 247953.70 209757.09 182260.56 4043168.61 2.69
2023-12-11 468298.44 67979.49 97574.33 109076.73 249108.11 2172095.38 243459.16 252681.14 209929.96 180250.58 4050453.32 0.18
2023-12-12 469772.65 69450.64 96426.39 108527.91 247430.61 2129314.58 241029.74 253707.28 210304.52 181783.62 4007747.96 -1.05
2023-12-13 474195.16 70002.33 98096.11 110140.06 249632.31 2163830.82 245888.58 260377.04 209238.46 182839.70 4064240.57 1.41

1240 rows × 12 columns

In [10]:
#Plot data:

plotly_line(portfolio_df, portfolio_df['Total Value [$]'], "Portfolio Total Value")
log_plotly_line(portfolio_df, portfolio_df['Total Value [$]'], "Portfolio Total Value - Log Scale")
plotly_line(portfolio_df, portfolio_df['Daily Return [%]'], "Daily Return [%]")
In [11]:
plotly_data(portfolio_df, "Equal Weighted Porfolio")
log_plotly_data(portfolio_df, "Equal Weighted Porfolio")

6.2 Random Weighted Portfolio¶

6.2.1 Define a function to generate random weights¶

In [12]:
def rand_weights(n):
    ''' Produces n random weights that sum to 1 '''
    k = np.random.rand(n)
    return k / sum(k)
In [13]:
#obtain weights vector
n_assets = len(Stock_Prices_df.columns)

weights = rand_weights(n_assets).round(4)
display(weights)
sum(weights)
array([0.1326, 0.001 , 0.1646, 0.0679, 0.1769, 0.0038, 0.1017, 0.0664,
       0.1274, 0.1576])
Out[13]:
0.9999

6.2.2 Asset Allocation¶

In [14]:
#Eqw     --  Equal Weighted </p>
#Rdw_1 -- Random Weighted 1 </p>
#Rdw_2 -- Random Weighted 2 [...]
In [15]:
def random_port_generate(initial_investment, n_runs):
    #obtain weights vector
    n_assets = len(Stock_Prices_df.columns)
    #lock vector to test function
    eq_weights = np.ones(n_assets) * 1/n_assets
    #Asset Allocation with parameters defined
    Eqw_df = asset_allocation(Stock_Prices_df, initial_investment, eq_weights)[['Total Value [$]', 'Daily Return [%]']]
    Eqw_df = Eqw_df.rename({'Total Value [$]':'Eqw [$]', 'Daily Return [%]':'Eqw [%]'}, axis="columns")
    All_df = Eqw_df
    
    # Placeholder to store all weights
    weights_runs = np.zeros((n_runs, n_assets))
    
    for i in range(n_runs):
        # Generate random weights 
        weights = rand_weights(n_assets)
        # Store the weights
        weights_runs[i,:] = weights
        # Random Asset Allocation
        df = asset_allocation(Stock_Prices_df, initial_investment, weights)[['Total Value [$]', 'Daily Return [%]']]
        #rename columns for iterate
        Rdw = df.rename({'Total Value [$]':'Rdw_{}[$]'.format(i), 
                         'Daily Return [%]':'Rdw_{}[%]'.format(i)}, axis="columns")

        All_df = pd.merge(All_df, Rdw, on = 'Date')

        #All_df = Eqw_df.join(Rdw)

        print("Simulation Run = {}".format(i))   
        print("Weights = {}".format(weights_runs[i].round(3))) 
        print('\n')
        
    return All_df
In [16]:
df = random_port_generate(initial_investment, n_runs)
daily_returns_df = df.iloc[:, 1::2] 
total_values_df = df.iloc[:, 0::2] 
display(df.round(2))
Simulation Run = 0
Weights = [0.078 0.081 0.114 0.144 0.188 0.017 0.072 0.102 0.023 0.18 ]


Simulation Run = 1
Weights = [0.011 0.053 0.187 0.161 0.201 0.087 0.089 0.012 0.095 0.105]


Simulation Run = 2
Weights = [0.144 0.11  0.045 0.023 0.142 0.131 0.079 0.158 0.16  0.008]


Simulation Run = 3
Weights = [0.146 0.062 0.201 0.012 0.177 0.007 0.058 0.158 0.151 0.028]


Simulation Run = 4
Weights = [0.161 0.104 0.021 0.162 0.173 0.013 0.11  0.149 0.064 0.042]


Simulation Run = 5
Weights = [0.157 0.152 0.05  0.146 0.037 0.078 0.121 0.094 0.023 0.143]


Simulation Run = 6
Weights = [0.03  0.213 0.168 0.082 0.13  0.058 0.102 0.014 0.135 0.069]


Simulation Run = 7
Weights = [0.103 0.004 0.109 0.088 0.127 0.177 0.027 0.022 0.203 0.141]


Simulation Run = 8
Weights = [0.061 0.187 0.088 0.216 0.016 0.048 0.059 0.048 0.111 0.167]


Simulation Run = 9
Weights = [0.087 0.12  0.045 0.19  0.007 0.14  0.155 0.077 0.169 0.009]


Simulation Run = 10
Weights = [0.039 0.142 0.048 0.103 0.045 0.095 0.136 0.155 0.014 0.224]


Simulation Run = 11
Weights = [0.145 0.08  0.133 0.063 0.103 0.032 0.077 0.092 0.153 0.122]


Simulation Run = 12
Weights = [0.093 0.019 0.172 0.115 0.183 0.048 0.078 0.159 0.018 0.116]


Simulation Run = 13
Weights = [0.184 0.165 0.    0.079 0.086 0.09  0.175 0.056 0.107 0.057]


Simulation Run = 14
Weights = [0.088 0.148 0.055 0.035 0.126 0.185 0.103 0.066 0.161 0.031]


Simulation Run = 15
Weights = [0.158 0.196 0.037 0.143 0.104 0.054 0.01  0.053 0.048 0.197]


Simulation Run = 16
Weights = [0.288 0.103 0.045 0.01  0.284 0.056 0.116 0.03  0.007 0.061]


Simulation Run = 17
Weights = [0.183 0.145 0.157 0.091 0.129 0.066 0.032 0.149 0.043 0.007]


Simulation Run = 18
Weights = [0.111 0.022 0.083 0.267 0.142 0.009 0.04  0.198 0.093 0.035]


Simulation Run = 19
Weights = [0.104 0.197 0.109 0.038 0.026 0.119 0.068 0.172 0.057 0.108]


Eqw [$] Eqw [%] Rdw_0[$] Rdw_0[%] Rdw_1[$] Rdw_1[%] Rdw_2[$] Rdw_2[%] Rdw_3[$] Rdw_3[%] ... Rdw_15[$] Rdw_15[%] Rdw_16[$] Rdw_16[%] Rdw_17[$] Rdw_17[%] Rdw_18[$] Rdw_18[%] Rdw_19[$] Rdw_19[%]
Date
2018-12-14 1000000.00 0.00 1000000.00 0.00 1000000.00 0.00 1000000.00 0.00 1000000.00 0.00 ... 1000000.00 0.00 1000000.00 0.00 1000000.00 0.00 1000000.00 0.00 1000000.00 0.00
2018-12-17 994222.16 -0.58 986669.06 -1.33 990235.99 -0.98 999244.02 -0.08 989696.84 -1.03 ... 992686.81 -0.73 992639.11 -0.74 991807.83 -0.82 985813.02 -1.42 996598.92 -0.34
2018-12-18 991118.87 -0.31 990970.19 0.44 989207.48 -0.10 991363.92 -0.79 989325.99 -0.04 ... 996792.18 0.41 990347.36 -0.23 989111.27 -0.27 988962.66 0.32 991956.89 -0.47
2018-12-19 975518.87 -1.57 972598.37 -1.85 973909.57 -1.55 974206.35 -1.73 970500.65 -1.90 ... 974357.48 -2.25 968799.76 -2.18 971018.43 -1.83 971894.81 -1.73 976275.72 -1.58
2018-12-20 976470.66 0.10 973491.12 0.09 970427.20 -0.36 976201.94 0.20 974186.47 0.38 ... 976106.22 0.18 967921.79 -0.09 976074.34 0.52 975469.72 0.37 982114.60 0.60
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2023-12-07 3937368.64 1.12 2315441.90 0.16 3363535.73 1.13 4809275.02 1.24 2415393.28 -0.32 ... 3046273.08 0.83 3816767.24 0.80 3428285.62 0.65 2288196.02 -0.03 4207638.81 1.13
2023-12-08 4043168.61 2.69 2339727.46 1.05 3460941.36 2.90 4946458.68 2.85 2426488.55 0.46 ... 3106006.72 1.96 3881391.59 1.69 3498381.90 2.04 2303808.16 0.68 4328503.66 2.87
2023-12-11 4050453.32 0.18 2339308.69 -0.02 3464478.60 0.10 4958629.03 0.25 2432470.56 0.25 ... 3103531.01 -0.08 3876988.37 -0.11 3506544.68 0.23 2308727.79 0.21 4341764.59 0.31
2023-12-12 4007747.96 -1.05 2331136.56 -0.35 3421863.64 -1.23 4903761.07 -1.11 2428515.11 -0.16 ... 3086325.63 -0.55 3851971.76 -0.65 3479876.07 -0.76 2304022.85 -0.20 4295236.48 -1.07
2023-12-13 4064240.57 1.41 2361363.43 1.30 3467915.95 1.35 4972940.79 1.41 2457119.44 1.18 ... 3123709.81 1.21 3899899.70 1.24 3529376.42 1.42 2335442.76 1.36 4360441.73 1.52

1240 rows × 42 columns

6.2.2.1 Plotting Data¶

In [17]:
plotly_data(total_values_df, "Portfolios Total Value[$]")
log_plotly_data(total_values_df, "Portfolios Total Value[$]")
plotly_data(daily_returns_df, "Portfolio Daily Returns [%]")
In [18]:
import plotly.express as px
# Plot histograms for stocks daily returns using plotly express
fig = px.histogram(daily_returns_df)
fig.update_layout({'plot_bgcolor': "white"})